// Windows/FileFind.cpp

#include "StdAfx.h"

#include "FileFind.h"
#include "Common/Stream/FileIO.h"

namespace Common {

#ifdef SUPPORT_DEVICE_FILE
bool IsDeviceName(LPCTSTR n);
#endif

namespace FileSystem {

#if defined(WIN_LONG_PATH)
#define WIN_LONG_PATH2
#endif

bool GetLongPath(LPCTSTR fileName, CString &res);

namespace Find {

#define WIN_FD_TO_MY_FI(fi, fd) \
  fi->Attributes = fd.dwFileAttributes; \
  fi->CreationTime = fd.ftCreationTime; \
  fi->AccessTime = fd.ftLastAccessTime; \
  fi->ModificationTime = fd.ftLastWriteTime; \
  fi->Size = (((UINT64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \
  fi->IsDevice = false;


static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, File::CFileInfo *pfi)
{
	ATLASSERT(NULL != pfi);
  WIN_FD_TO_MY_FI(pfi, fd);
  pfi->Name = fd.cFileName;
}

  
////////////////////////////////
// CFindFile

bool CFindFile::Close()
{
  if (_handle == INVALID_HANDLE_VALUE)
    return true;
  if (!::FindClose(_handle))
    return false;
  _handle = INVALID_HANDLE_VALUE;
  return true;
}

          
bool CFindFile::FindFirst(LPCTSTR wildcard, File::CFileInfo *pfi)
{
  if (!Close())
    return false;
  WIN32_FIND_DATA fd;
  _handle = ::FindFirstFile(wildcard, &fd);
  #ifdef WIN_LONG_PATH2
  if (_handle == INVALID_HANDLE_VALUE)
  {
    CString longPath;
    if (GetLongPath(wildcard, longPath))
      _handle = ::FindFirstFileW(longPath, &fd);
  }
  #endif
  if (_handle == INVALID_HANDLE_VALUE)
    return false;
  ConvertWIN32_FIND_DATA_To_FileInfo(fd, pfi);
  return true;
}


bool CFindFile::FindNext(File::CFileInfo *pfi)
{
  WIN32_FIND_DATA fd;
  bool result = BOOLToBool(::FindNextFile(_handle, &fd));
  if (result)
    ConvertWIN32_FIND_DATA_To_FileInfo(fd, pfi);
  return result;
}


bool DoesFileExist(LPCTSTR name)
{
  File::CFileInfo fi;
  return fi.Find(name) && !fi.IsDir();
}

bool DoesDirExist(LPCTSTR name)
{
  File::CFileInfo fi;
  return fi.Find(name) && fi.IsDir();
}

bool DoesFileOrDirExist(LPCTSTR name)
{
  File::CFileInfo fi;
  return fi.Find(name);
}


/////////////////////////////////////
// CEnumerator

bool CEnumerator::NextAny(File::CFileInfo *pfi)
{
  if (_findFile.IsHandleAllocated())
    return _findFile.FindNext(pfi);
  else
    return _findFile.FindFirst(_wildcard, pfi);
}

bool CEnumerator::Next(File::CFileInfo *pfi)
{
	ATLASSERT(NULL != pfi);
  for (;;)
  {
    if (!NextAny(pfi))
      return false;
    if (!pfi->IsDots())
      return true;
  }
}

bool CEnumerator::Next(File::CFileInfo *pfi, bool &found)
{
  if (Next(pfi))
  {
    found = true;
    return true;
  }
  found = false;
  return (::GetLastError() == ERROR_NO_MORE_FILES);
}

////////////////////////////////
// CFindChangeNotification
// FindFirstChangeNotification can return 0. MSDN doesn't tell about it.

bool CFindChangeNotification::Close()
{
  if (!IsHandleAllocated())
    return true;
  if (!::FindCloseChangeNotification(_handle))
    return false;
  _handle = INVALID_HANDLE_VALUE;
  return true;
}
           
HANDLE CFindChangeNotification::FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter)
{
  _handle = ::FindFirstChangeNotification(pathName, BoolToBOOL(watchSubtree), notifyFilter);
  #ifdef WIN_LONG_PATH2
  if (!IsHandleAllocated())
  {
    CString longPath;
    if (GetLongPath(pathName, longPath))
      _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter);
  }
  #endif
  return _handle;
}


bool MyGetLogicalDriveStrings(CStringVector &driveStrings)
{
  driveStrings.clear();
  UINT32 size = GetLogicalDriveStrings(0, NULL);
  if (size == 0)
    return false;
  CString buffer;
  UINT32 newSize = GetLogicalDriveStrings(size, buffer.GetBuffer(size));
  size++;
  if (newSize == 0)
    return false;
  if (newSize > size)
    return false;
  CString string;
  LPTSTR pointer = buffer.GetBuffer(size);
  for (UINT32 i = 0; i < newSize; i++)
  {
    TCHAR c = *(pointer + i);
    if (c == TEXT('\0'))
    {
		driveStrings.push_back(string);
      string.Empty();
    }
    else
      string += c;
  }
  if (!string.IsEmpty())
    return false;
  return true;
}



}}}
